/**
 * Author xiaol
 * Rev
 * Date: 2021-03-23 21:38
 * <p>
 * Copyright (C) 2021 Seeyon, Inc. All rights reserved.
 * <p>
 * This software is the proprietary information of Seeyon, Inc.
 * Use is subject to license terms.
 * @company seeyon.com
 * @author xiaol
 */
package com.lagou.bean;

import com.google.common.collect.Maps;
import com.lagou.annotate.Autowired;
import com.lagou.annotate.Transactional;
import com.lagou.proxy.ProxyFactory;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @description
 * @date 2021-03-23 21:38
 * @author xiaol
 */
public class AnnotateBeanApplicaitonContext implements AbstractApplicationContext{
	/**
	 * 一级缓存
	 */
	private Map<String,Object> singtonObjects = Maps.newConcurrentMap();

	private Map<String,Class> map;

	public AnnotateBeanApplicaitonContext(Map<String, Class> map) {
		this.map = map;
	}

	public void createBean() {
		map.forEach((k,v)->{
			doCreateBean(v);
		});
	}

	private Object doCreateBean(Class clazz){
		Object newInstance = null;
		try {
			String beanName = lowerFirstCase(clazz.getSimpleName());
			newInstance = clazz.newInstance();
			//填充属性
			populateBean(beanName,newInstance);
			if(isAop(clazz)){
				newInstance = ProxyFactory.createProxy(newInstance);
			}
			Class[] interfaces = clazz.getInterfaces();
			for (int i = 0; i < interfaces.length; i++) {
				String id = lowerFirstCase(interfaces[i].getSimpleName());
				singtonObjects.put(id, newInstance);
			}
		} catch (Exception e) {
		}
		return newInstance;
	}

	/**
	 * 填充属性
	 * @param beanName
	 * @param newInstance
	 */
	private void populateBean(String beanName, Object newInstance) {
		Class clazz = newInstance.getClass();
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			if(declaredField.isAnnotationPresent(Autowired.class)){
				//根据type类型来注入bean
				String sid = declaredField.getName();
				Object bean = singtonObjects.get(sid);
				if(bean == null){
					bean = doCreateBean(map.get(sid));
				}
				try {
					PropertyDescriptor propertyDescriptor = new PropertyDescriptor(sid,clazz);
					Method writeMethod = propertyDescriptor.getWriteMethod();
					writeMethod.invoke(newInstance,bean);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 是否开启了事务
	 * @param clazz
	 * @return
	 */
	private boolean isAop(Class clazz) {
		if (clazz.isAnnotationPresent(Transactional.class)) {
			return true;
		}
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for (Method declaredMethod : declaredMethods) {
			if(declaredMethod.isAnnotationPresent(Transactional.class)){
				return true;
			}
		}
		return false;
	}


	public static String lowerFirstCase(String str){
		char[] chars = str.toCharArray();
		chars[0] +=32;
		return String.valueOf(chars);
	}
}
